package com.quick.develop.flowable.application.impl;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import com.quick.develop.flowable.domain.dto.model.BpmModelElementsDTO;
import com.quick.develop.flowable.domain.vo.rule.BpmRuleReqVO;
import com.quick.develop.flowable.domain.vo.rule.BpmRuleRespVO;
import com.quick.develop.framework.utils.StringUtils;
import com.quick.develop.flowable.application.IBpmRuleApplication;
import com.quick.develop.flowable.domain.rule.BpmProcessRule;
import com.quick.develop.flowable.framework.convert.definition.BpmProcessRuleConvert;
import com.quick.develop.flowable.utils.FlowableUtil;
import com.quick.develop.flowable.service.definition.IBpmModelService;
import com.quick.develop.flowable.service.definition.IBpmProcessDefinitionService;
import com.quick.develop.flowable.service.process.IBpmProcessRuleService;
import com.quick.develop.flowable.service.task.IBpmTaskService;
import lombok.extern.slf4j.Slf4j;
import org.flowable.bpmn.model.BpmnModel;
import org.flowable.bpmn.model.CallActivity;
import org.flowable.bpmn.model.UserTask;
import org.flowable.common.engine.api.FlowableException;
import org.flowable.engine.repository.ProcessDefinition;
import org.flowable.task.api.history.HistoricTaskInstance;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.util.*;

import static com.quick.develop.flowable.enums.BpmErrorCodeConstants.MODEL_NOT_EXISTS;
import static com.quick.develop.flowable.enums.BpmErrorCodeConstants.PROCESS_DEFINITION_NOT_EXISTS_OR_ACTIVITY;
import static com.quick.develop.framework.common.utils.ServiceExceptionUtils.exception;

/**
 * @author junyuan.liu
 * @description: 
 * @date 2022/5/24 14:14
 */
@Component
@Slf4j
public class BpmRuleApplicationImpl implements IBpmRuleApplication {
    @Resource
    private IBpmProcessRuleService bpmProcessRuleService;

    @Resource
    private IBpmProcessDefinitionService bpmProcessDefinitionService;

    @Resource
    private IBpmModelService bpmModelService;

    @Resource
    private BpmModelApplicationImpl bpmModelApplicationImpl;
    
    @Resource
    private IBpmTaskService bpmTaskService;

    @Override
    public List<BpmRuleRespVO> getBpmProcessRules(BpmRuleReqVO reqVo) {
        if (StrUtil.isEmpty(reqVo.getModelId()) && StrUtil.isEmpty(reqVo.getProcessDefinitionId()) ){
            return Collections.emptyList();
        }
        List<BpmProcessRule> rules = Collections.emptyList();
        BpmnModel model = new BpmnModel();
        String modeId = reqVo.getModelId();
        String proDefId = reqVo.getProcessDefinitionId();
        if(StrUtil.isNotEmpty(proDefId)){
            model = bpmProcessDefinitionService.getBpmnModelByDefinitionId(proDefId);
        }else if (StrUtil.isNotEmpty(modeId)){
            model = bpmModelService.getBpmnModelByModelId(modeId);
        }
        if (ObjectUtil.isEmpty(model)){
            throw exception(MODEL_NOT_EXISTS);
        }
        
        // rules = bpmProcessRuleService.getRulesByModelId(modeId);
        rules = bpmProcessRuleService.getRulesByModelKey(reqVo.getModelKey());
        
        List<UserTask> userTasks = FlowableUtil.getBpmnModelElements(model, UserTask.class);
        List<CallActivity> activities = FlowableUtil.getBpmnModelElements(model, CallActivity.class);
        if (CollUtil.isEmpty(userTasks)) {
            return Collections.emptyList();
        }

        return BpmProcessRuleConvert.INSTANCE.convertList(userTasks, activities, rules, reqVo);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void handleBpmProcessRules(List<BpmProcessRule> rules) {
        if (CollUtil.isEmpty(rules)){
            return;
        }
        Optional.of(rules).ifPresent(bpmProcessRuleService::saveProcessRuleList);
        /*rules.forEach(r -> {
            if (ObjectUtil.isEmpty(r.getId())){
                bpmProcessRuleService.createProcessRule(r);
            }else{
                bpmProcessRuleService.updateProcessRule(r);
            }
        });*/
    }

    @Override
    public BpmProcessRule getTaskRule(String modelKey, String nodeCode) {
        log.info("获取节点规则模型key：{}，节点：{}", modelKey, nodeCode);
        List<BpmProcessRule> list = bpmProcessRuleService.getRulesByKeyNode(modelKey, nodeCode);
        if (CollUtil.isEmpty(list)){
            log.error("===获取流程任务人员分配规则为空==");
            throw new FlowableException(StrUtil.format("流程任务({}/{}) 找不到符合的任务规则",
                    modelKey, nodeCode));
        }
        if (list.size() > 1){
            log.error("===流程任务人员分配规则多条===");
            throw new FlowableException(StrUtil.format("流程任务({}/{}) 找到过多任务规则({})",
                    modelKey, nodeCode, list.size()));
        }
        return list.get(0);
    }

    @Override
    public BpmProcessRule getFirstNodeRule(String modelKey) {
        ProcessDefinition definition = bpmProcessDefinitionService.getActiveProcessDefinitionByKey(modelKey);
        Optional.ofNullable(definition)
                .orElseThrow(() ->
                        exception(PROCESS_DEFINITION_NOT_EXISTS_OR_ACTIVITY,
                                modelKey));

        BpmnModel bpmnModel = bpmProcessDefinitionService.getBpmnModelByDefinitionId(definition.getId());
        UserTask firstUserTask = FlowableUtil.getFirstUserTask(bpmnModel);
        log.info("获取第一个节点规则，节点：{}", firstUserTask.getId());
        return this.getTaskRule(modelKey, firstUserTask.getId());
    }

    @Override
    public BpmRuleRespVO getBpmRule(BpmRuleReqVO reqVo) {
        BpmProcessRule rule = new BpmProcessRule();
        List<HistoricTaskInstance> hisTaskList = new ArrayList<>();
        if (StringUtils.isNotEmpty(reqVo.getNodeCode())) {
            rule = getTaskRule(reqVo.getModelKey(), reqVo.getNodeCode());
            if (StringUtils.isNotEmpty(reqVo.getProcessInstanceId())) {
                hisTaskList = bpmTaskService.getTasksFinishedByProcessInstanceId(reqVo.getProcessInstanceId());
            }
        }else if (StringUtils.isNotEmpty(reqVo.getModelKey())){
            rule = getFirstNodeRule(reqVo.getModelKey());
        }

        return BpmProcessRuleConvert.INSTANCE.convertVo(rule, hisTaskList);
    }

    @Override
    public List<BpmProcessRule> getBpmProcessRules4SetUp(BpmRuleReqVO reqVo) {
        List<BpmProcessRule> listBpm = bpmProcessRuleService.getRulesByModeKey(reqVo.getModelKey());
        List<BpmModelElementsDTO> listNow =bpmModelApplicationImpl.getModelElementsByProcessDefinitionKey(reqVo.getModelKey());
//        Map<String,BpmModelElementsDTO> map = new HashMap<>();
//        for(BpmModelElementsDTO obj:listNow){
//            map.put(obj.getId(),obj);
//        }
//        List<BpmProcessRule> listBpmProcessRule = new ArrayList<>();
//        for(BpmProcessRule obj:listBpm){
//            if(map.get(obj.getNodeCode())!=null){
//                listBpmProcessRule.add(obj);
//            }
//        }

        Map<String,BpmProcessRule> map = new HashMap<>();

        for(BpmProcessRule obj:listBpm){
            map.put(obj.getNodeCode(),obj);
        }

        List<BpmProcessRule> listBpmProcessRule = new ArrayList<>();
        for(BpmModelElementsDTO obj:listNow){
            if(map.get(obj.getId())!=null){
                listBpmProcessRule.add(map.get(obj.getId()));
            }
        }
        return listBpmProcessRule;
    }

    @Override
    public List<BpmProcessRule> getBpmProcessRulesList(BpmRuleReqVO reqVo) {
        List<BpmProcessRule> listBpm = bpmProcessRuleService.getRulesListByModeKey(reqVo.getModelKey());
        List<BpmModelElementsDTO> listNow =bpmModelApplicationImpl.getModelElementsByProcessDefinitionKey(reqVo.getModelKey());
        Map<String,BpmModelElementsDTO> map = new HashMap<>();
        for(BpmModelElementsDTO obj:listNow){
            map.put(obj.getId(),obj);
        }
        List<BpmProcessRule> listBpmProcessRule = new ArrayList<>();
        for(BpmProcessRule obj:listBpm){
            if(map.get(obj.getNodeCode())!=null){
                listBpmProcessRule.add(obj);
            }
        }

        return listBpmProcessRule;
    }
}
